home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
EuroCD 3
/
EuroCD 3.iso
/
MIDI
/
Euterpe_Installation
/
Euterpe
/
Developer
/
fxdemo.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-06-24
|
20KB
|
696 lines
/* FXDEMO
* A simple example for an EFFECTSPROCESSOR xapp application
* Author : Dominique Lorre
* $Id$
*/
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/semaphores.h>
#include <intuition/gadgetclass.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/gadtools.h>
#include <clib/macros.h>
#include "/xappmsg.h"
//#include "mydebug.h"
struct FXNode* n ;
struct List* l ;
struct Window* xappwin ;
struct Gadget* glist ;
struct Gadget* gad ;
APTR vi ;
struct Screen *ps ;
BOOL winopen ;
struct MsgPort* MyPort ;
struct MsgPort* XAppPort ;
struct MsgPort* replyPort ;
struct Hook FXProcessor ;
char MyPortName[XAPPNAMELENGTH] ;
APTR xHandle ;
struct TagItem ti[] = {
{XAPP_Name, (ULONG)"FX-DEMO"},
{XAPP_PortName, (ULONG)MyPortName},
{XAPP_Type, (ULONG)XAPPTYP_EFFECTSPROCESSOR},
{FX_Hook, (ULONG)&FXProcessor},
{TAG_DONE}
};
struct TagItem th[] = {
{XAPP_Handle, NULL},
{TAG_DONE}
};
struct fxDemoData {
struct SignalSemaphore *fxd_Semaphore ;
LONG fxd_Len ;
WORD fxd_Repeat ;
WORD fxd_Variation ;
WORD fxd_Volume ;
BOOL fxd_ByPass ;
} fData ;
struct SignalSemaphore *MySemaphore ;
static ULONG winsig, msig, signal ;
BOOL fin ;
#define HIGHPRI 21
struct XAppMsg *xmsg, *replymsg, *emsg ;
extern __stdargs ULONG HookEntry() ;
VOID main(VOID) ;
BOOL InitAll(void) ;
BOOL CloseAll(WORD level) ;
void ProcessEvents(void) ;
BOOL InitWin(void) ;
BOOL CloseWin(WORD level) ;
void ProcessWinEvents(void) ;
VOID StripWindow(struct Window *win) ;
VOID StripIntuiMessages( struct MsgPort *mp, struct Window *win ) ;
VOID StripMessages( struct MsgPort *mp ) ;
ULONG __saveds __asm FX(register __a0 struct Hook *h,
register __a2 APTR Object,
register __a1 struct FXParams *fp) ;
/****** fxdemo.c/FX ********************************************************
*
* NAME
* FX -- Hook function for effects processing
*
* SYNOPSIS
* result = FX(h, Object, fp)
* ULONG __stdargs FX(struct Hook *, APTR, struct FXParams *)
*
* FUNCTION
* This is the Hook function that will be invoked by Euterpe when processing
* events for effects.
*
* INPUTS
* h The Hook initialized by this module
* Object Currently NULL
* fp A FXParams structure described in xappmsg.h
*
* RESULTS
* result 1 if everything ok and 0 if failure (other effects will be
* skipped)
*
* NOTES
* All the new events have to be placed on the same List, the best way
* to do this is to use exec.library/AddHead(). Do NOT expect the list
* to be sorted by time since effects might be chained. This function
* will be called in the heart of a realtime process running at a high
* priority (actually 21), so slow functions involving disk access,
* keyboard input or the like must be invoked externally, by signalling.
* If your window is providing parameters, a Semaphore protection
* will be needed.
* NEVER EVER INSERT NOTEOFF EVENTS IN THE LIST
* Inserting a NoteOff is a bad idea because these events must be linked to
* the correct NoteOn. For the control of NoteOff, use the x_Len field which
* set the duration of a NoteOn and the x_Data[3] field which set the
* velocity of the released note.
* NoteOff events will be inserted by Euterpe at the end of the FX parsing.
* NoteOn with x_Len = 0 are subject to crashes
* DO NOT EXPECT TO UNDERSTAND ALL EVENTS
* Events will be documented in the future because some are really special
* such as tempochanges. For allowing your effect to work in the future, just
* keep the unknowned events unchanged. You can of course modify their pos
* but timesig events might be moved by Euterpe at the beginning of a
* measure.
* DO NOT MAKE ANY ASSUMPTIONS ON WHAT WILL BE DONE WITH THIS EFFECT
* If you can. Because the effects might be modified in the future
* to operate on input flow, to write themselves to a track or even to be
* lended as services to other tasks.
* SEE ALSO
* utility/hooks.h utility.library/CallHookPkt
****************************************************************************
*
*/
ULONG __saveds __asm FX(register __a0 struct Hook *h,
register __a2 APTR Object,
register __a1 struct FXParams *fp)
{
struct FXNode *n, *c, *s ;
struct fxDemoData *fxd ;
UBYTE type ;
LONG val, r ;
fxd = (struct fxDemoData *)h->h_Data ;
// Semaphore protection is a must if you want to allow the user
// to modify the parameters while playing.
ObtainSemaphore(fxd->fxd_Semaphore) ;
if (!IsListEmpty((struct List *)fp->fp_List)) {
// if we are the first effect the list will contain one event
// the linked effects will found what has been put by their
// predecessors
n = (struct FXNode *)fp->fp_List->mlh_Head ;
while (n->x_Node.mln_Succ) {
s = (struct FXNode *)n->x_Node.mln_Succ ;
if (!fxd->fxd_ByPass) {
type = n->x_Data[0] & 0xF0 ;
// This is a noteon
if (type == 0x90) {
// Note transposition
val = n->x_Data[1] + fxd->fxd_Variation ;
n->x_Data[1] = (val > 0) ? MIN(val, 0x7F) : 0 ;
for (r = 0; (r < fxd->fxd_Repeat); r++) {
// Since we are in a RealTime application
// we do not lose time by doing some garbage
// collection. This code is safe because the array
// will be reset by Euterpe when a new event will
// appear
fp->fp_Pos++ ;
c = (fp->fp_Pos < fp->fp_Size) ? &(fp->fp_Array[fp->fp_Pos]) : NULL ;
if (c) { // We have the new event node
c->x_Msg = n->x_Msg ; // fast copy of the data
if (fxd->fxd_Len>1) {
c->x_Pos = n->x_Pos + fxd->fxd_Len ;
c->x_Len = n->x_Len ;
if (n->x_Len > fxd->fxd_Len) {
n->x_Len = fxd->fxd_Len-1 ; // len > 0 !
}
}
// Volume handling
if (fxd->fxd_Volume > 0) {
val = c->x_Data[2] * (fxd->fxd_Volume+1) ;
c->x_Data[2] = MIN(val, 0x7F) ;
val = c->x_Data[3] * (fxd->fxd_Volume+1) ;
c->x_Data[3] = MIN(val, 0x7F) ;
}
else if (fxd->fxd_Volume < 0) {
c->x_Data[2] /= -fxd->fxd_Volume+1 ;
c->x_Data[3] /= -fxd->fxd_Volume+1 ;
}
// Add to list
// Keeping the list sorted might allow quicker
// effects if the sorting operation is really fast
AddHead((struct List *)fp->fp_List, (struct Node *)c) ;
}
}
}
}
n = s ;
}
}
ReleaseSemaphore(fxd->fxd_Semaphore) ;
return 1 ;
}
VOID main(VOID)
{
int retval ;
if (InitAll()) {
ProcessEvents() ;
retval = CloseAll(0)?RETURN_OK:RETURN_FAIL;
}
else
retval = RETURN_FAIL ;
exit(retval) ;
}
/****** fxdemo.c/InitAll ***************************************************
*
* NAME
* InitAll -- Main Initializations
*
* SYNOPSIS
* success = InitAll()
* BOOL InitAll(void)
*
* FUNCTION
* InitAll allocates all needed resources step by step and is responsible
* for the initialization of the communication with Euterpe. CloseAll will
* be called in case of failure.
*
* INPUTS
* None
*
* RESULTS
* success TRUE if the initialization succeed, FALSE if not
*
* BUGS
* This function has a weak point because it may fail if Euterpe is closing
* when this task is loading. Forbid()/Permit() might solve the problem.
* Anyway, under normal use effects will not be loaded when Euterpe is
* exiting and developers have been asked not to use Forbid(). If
* someone has an idea...
*
* SEE ALSO
* fxdemo.c/CloseAll
****************************************************************************
*
*/
BOOL InitAll(void)
{
// First, create a ReplyPort for communication with Euterpe
replyPort = CreateMsgPort() ;
if (!replyPort)
return CloseAll(1) ;
// Now, initialize the Message
if (xmsg = (struct XAppMsg *)AllocVec(sizeof(struct XAppMsg), MEMF_CLEAR|MEMF_PUBLIC)) {
xmsg->xm_Message.mn_Node.ln_Type = NT_MESSAGE ;
xmsg->xm_Message.mn_Length = sizeof( struct XAppMsg ) ;
xmsg->xm_Message.mn_ReplyPort = replyPort ;
}
else
return CloseAll(2) ;
// Our private semaphore
if (MySemaphore = (struct SignalSemaphore *)AllocVec(sizeof(struct SignalSemaphore), MEMF_CLEAR|MEMF_PUBLIC)) {
InitSemaphore(MySemaphore) ;
}
else
return CloseAll(3) ;
// Parameters initialization
fData.fxd_Semaphore = MySemaphore ;
fData.fxd_Repeat = 2 ;
fData.fxd_Len = 192 ;
fData.fxd_Volume = -2 ;
fData.fxd_Variation = 12 ;
fData.fxd_ByPass = FALSE ;
// Hook initialization
FXProcessor.h_Entry = (HOOKFUNC)FX ;
FXProcessor.h_SubEntry = NULL ;
FXProcessor.h_Data = &fData ;
// Look at Euterpe's xapp port
if (XAppPort = FindPort((UBYTE *)XAPPPORTNAME)) {
// Euterpe is here, let's register
xmsg->xm_Action = XAPPACT_ADD ;
xmsg->xm_Tags = ti ;
PutMsg(XAppPort, (struct Message *)xmsg);
WaitPort(replyPort) ;
replymsg = (struct XAppMsg *)GetMsg(replyPort) ;
// Euterpe has given us a name for our port
xHandle = replymsg->xm_Result ;
if (!xHandle)
return CloseAll(4) ;
th[0].ti_Data = (ULONG)xHandle ;
}
else
return CloseAll(4) ;
// Now, create our Port with the name given by Euterpe
MyPort = CreatePort(MyPortName, NULL);
// We are ready now, tell Euterpe how the init was
xmsg->xm_Action = MyPort ? XAPPACT_INIT : XAPPACT_FAILURE ;
xmsg->xm_Tags = th ;
PutMsg(XAppPort, (struct Message *)xmsg);
WaitPort(replyPort) ;
replymsg = (struct XAppMsg *)GetMsg(replyPort) ;
if (!MyPort)
return CloseAll(5) ;
msig = 1 << MyPort->mp_SigBit ;
return TRUE ;
}
/****** fxdemo.c/CloseAll **************************************************
*
* NAME
* CloseAll -- Deallocates the resources
*
* SYNOPSIS
* success = CloseAll(level)
* BOOL CloseAll(WORD)
*
* FUNCTION
* CloseAll() will deallocate all the successfully allocated resources
*
* INPUTS
* level 0 if normal deallocation or the step where allocation failed
*
* RESULTS
* success TRUE if called with level=0 FALSE in other cases
*
****************************************************************************
*
*/
BOOL CloseAll(WORD level)
{
switch(level) {
case 0:
DeletePort(MyPort) ;
case 5:
case 4:
FreeVec(MySemaphore) ;
case 3:
FreeVec(xmsg) ;
case 2:
DeleteMsgPort(replyPort) ;
}
return (BOOL) ( level ? FALSE : TRUE ) ;
}
void ProcessEvents(void)
{
ULONG sigmask ;
BOOL end ;
enum XAppAction action ;
end = winopen = FALSE ;
while (!end) {
sigmask = msig ;
if (winopen) sigmask |= winsig ;
signal = Wait(sigmask) ;
if (signal & winsig) {
// A better way of doing this could be to place the semaphore
// calls at the beginning of each modification of the fData fields
ObtainSemaphore(MySemaphore) ;
ProcessWinEvents() ;
if (!winopen)
CloseWin(0) ;
ReleaseSemaphore(MySemaphore) ;
}
if (signal & msig) {
while (emsg = (struct XAppMsg *)GetMsg(MyPort)) {
action = emsg->xm_Action ;
// Do not reply to a remove msg
// since Euterpe do not wait for it
if (action != XAPPACT_REMOVE)
ReplyMsg((struct Message *)emsg) ;
switch (action) {
case XAPPACT_SHOW:
if (!winopen && InitWin())
winopen = TRUE ;
break ;
case XAPPACT_REMOVE:
end = TRUE ;
case XAPPACT_HIDE:
if (winopen) {
CloseWin(0) ;
}
break ;
}
}
}
}
}
LONG __saveds VolumeLevel(struct Gadget *g, WORD level)
{
static char s[30] ;
static char v[8] ;
if (level < 0) {
stci_d(v, -level+1) ;
strcpy(s, "Divide Volume by ") ;
strcat(s, v) ;
}
else if (level > 0) {
stci_d(v, level+1) ;
strcpy(s, "Multiply Volume by ") ;
strcat(s, v) ;
}
else strcpy(s, "Keep Volume") ;
return (LONG)s ;
}
BOOL InitWin(void)
{
struct NewGadget ng ;
UWORD winZoom[4] = { 50, 50, 300, 20 } ;
ps = LockPubScreen((UBYTE *)"Euterpe") ; // Get the Euterpe public screen
if (!ps)
return CloseWin(1) ;
winZoom[0] = winZoom[3] = ps->BarHeight ; // cosmetic details
vi = GetVisualInfo(ps, NULL) ;
if (!vi)
return CloseWin(2) ;
glist = NULL ;
gad = CreateContext(&glist) ;
if (!gad)
return CloseWin(3) ;
ng.ng_VisualInfo = vi ;
ng.ng_TextAttr = ps->Font ;
ng.ng_TopEdge = ps->BarHeight + 8 ; // font adaptative method
ng.ng_LeftEdge = 20 ;
ng.ng_Width = 10 ;
ng.ng_Height = ng.ng_TextAttr->ta_YSize * 3 / 2 ;
ng.ng_GadgetID = 1 ;
ng.ng_GadgetText = (UBYTE *)"ByPass" ;
ng.ng_Flags = PLACETEXT_RIGHT ;
gad = CreateGadget(CHECKBOX_KIND, gad, &ng,
GTCB_Checked, fData.fxd_ByPass,
TAG_DONE);
if (!gad)
return CloseWin(4);
ng.ng_TopEdge += ng.ng_Height + 8 ;
ng.ng_LeftEdge = 20 ;
ng.ng_Width = 128 ;
ng.ng_Height = 20 ;
ng.ng_GadgetID = 2 ;
ng.ng_Flags = PLACETEXT_RIGHT ;
gad = CreateGadget(SLIDER_KIND, gad, &ng,
GTSL_Min, -64,
GTSL_Max, 64,
GTSL_Level, fData.fxd_Variation,
GTSL_LevelFormat, "Transposition : %ld",
GTSL_MaxLevelLen, 20,
GTSL_LevelPlace, PLACETEXT_RIGHT,
GA_RelVerify, TRUE,
TAG_DONE);
if (!gad)
return CloseWin(4);
ng.ng_TopEdge += ng.ng_Height + 8 ;
ng.ng_GadgetID = 3 ;
gad = CreateGadget(SLIDER_KIND, gad, &ng,
GTSL_Min, 0,
GTSL_Max, 192*4,
GTSL_Level, fData.fxd_Len,
GTSL_LevelFormat, "Delay Length : %ld",
GTSL_MaxLevelLen, 19,
GTSL_LevelPlace, PLACETEXT_RIGHT,
GA_RelVerify, TRUE,
TAG_DONE);
if (!gad)
return CloseWin(4);
ng.ng_TopEdge += ng.ng_Height + 8 ;
ng.ng_GadgetID = 4 ;
ng.ng_Flags = PLACETEXT_RIGHT ;
gad = CreateGadget(SLIDER_KIND, gad, &ng,
GTSL_Min, 0,
GTSL_Max, 10,
GTSL_Level, fData.fxd_Repeat,
GTSL_LevelFormat, "Repeat : %ld times",
GTSL_MaxLevelLen, 19,
GTSL_LevelPlace, PLACETEXT_RIGHT,
GA_RelVerify, TRUE,
TAG_DONE);
if (!gad)
return CloseWin(4);
ng.ng_TopEdge += ng.ng_Height + 8 ;
ng.ng_GadgetID = 5 ;
ng.ng_Flags = PLACETEXT_RIGHT ;
gad = CreateGadget(SLIDER_KIND, gad, &ng,
GTSL_Min, -8,
GTSL_Max, 8,
GTSL_Level, fData.fxd_Volume,
GTSL_LevelFormat, "%s", /* Did you knew this trick ? :) */
GTSL_DispFunc, VolumeLevel,
GTSL_MaxLevelLen, 25,
GTSL_LevelPlace, PLACETEXT_RIGHT,
GA_RelVerify, TRUE,
TAG_DONE);
if (!gad)
return CloseWin(4);
/* Open a simple window */
xappwin = OpenWindowTags(NULL,
WA_Left, 50,
WA_Top, ps->BarHeight,
WA_Width, 300,
WA_Height, ng.ng_TopEdge + ng.ng_Height + 8, // font adaptative
WA_Title, MyPortName,
WA_CloseGadget, TRUE,
WA_DepthGadget, TRUE,
WA_Gadgets, glist,
WA_IDCMP, IDCMP_CLOSEWINDOW|IDCMP_REFRESHWINDOW|
CHECKBOXIDCMP|SLIDERIDCMP,
WA_DragBar, TRUE,
WA_Activate, TRUE,
WA_PubScreen, ps,
WA_Zoom, winZoom,
TAG_DONE) ;
if (xappwin) {
GT_RefreshWindow(xappwin, NULL) ;
winsig = 1 << xappwin->UserPort->mp_SigBit ;
return TRUE ;
}
else
return CloseWin(5) ;
}
BOOL CloseWin(WORD level)
{
winopen = FALSE ;
switch(level) {
case 0:
StripWindow(xappwin) ;
CloseWindow(xappwin) ;
xappwin = NULL ;
winsig = 0 ;
case 5:
case 4:
FreeGadgets(glist) ;
case 3:
FreeVisualInfo(vi) ;
case 2:
UnlockPubScreen(NULL, ps) ;
}
return (BOOL) ( level ? FALSE : TRUE ) ;
}
void ProcessWinEvents(void)
{
ULONG classe ;
UWORD code ;
struct IntuiMessage* imsg ;
struct Gadget* gad ;
while (imsg = GT_GetIMsg(xappwin->UserPort)) {
classe = imsg->Class ;
code = imsg->Code ;
gad = (struct Gadget *)imsg->IAddress ;
GT_ReplyIMsg(imsg) ;
switch (classe) {
case IDCMP_REFRESHWINDOW:
GT_BeginRefresh(xappwin) ;
GT_EndRefresh(xappwin, TRUE) ;
break ;
case IDCMP_GADGETUP:
switch (gad->GadgetID) {
case 1:
fData.fxd_ByPass = code ;
break ;
case 2:
fData.fxd_Variation = code ;
break ;
case 3:
fData.fxd_Len = code ;
break ;
case 4:
fData.fxd_Repeat = code ;
break ;
case 5:
fData.fxd_Volume = code ;
break ;
}
break ;
case IDCMP_CLOSEWINDOW:
winopen = FALSE ;
break ;
}
}
}
/* Useful C= functions */
VOID StripWindow(struct Window *win)
{
Forbid();
StripIntuiMessages(win->UserPort, win);
win->UserPort = NULL;
ModifyIDCMP(win, NULL);
Permit();
}
VOID StripIntuiMessages( struct MsgPort *mp, struct Window *win )
{
struct IntuiMessage *msg;
struct Node *succ;
msg = (struct IntuiMessage *) mp->mp_MsgList.lh_Head;
while( succ = msg->ExecMessage.mn_Node.ln_Succ ) {
if( msg->IDCMPWindow == win ) {
/* Intuition is about to free this message.
* Make sure that we have politely sent it back.
*/
Remove( (struct Node *)msg );
ReplyMsg( (struct Message *)msg );
}
msg = (struct IntuiMessage *) succ;
}
}
/* Another one for a private MsgPort */
VOID StripMessages( struct MsgPort *mp )
{
struct Message *msg;
struct Node *succ;
msg = (struct Message *) mp->mp_MsgList.lh_Head;
while( succ = msg->mn_Node.ln_Succ ) {
Remove( (struct Node *)msg );
ReplyMsg( msg );
msg = (struct Message *) succ;
}
}